﻿/*
 * SurfaceRenderer
 * 
 * initial implementation : 20.10.2008
 *
 * Copyright (c) Adam Sida, asida@centrum.cz.
 * All rights reserved.
 * 
 * for license see Petra.Gis.License.txt
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Drawing;
using System.Drawing.Drawing2D;
using Petra.Gis.Geometry;
using Petra.Gis.Geometry.Support;
using Petra.Gis.Geometry.Utils;
using Petra.Gis.DataServices;
using Petra.Gis.RenderingServices.PlanarBase;
using Petra.Gis.ReferenceSystemServices.Transformations;


namespace Petra.Gis.RenderingServices.GDIPlus
{

  public partial class SurfaceRenderer : EdgeGeomRenderer
  {


    protected SolidBrush _outFillBrush;



    /// <summary>
    /// xaml constr.
    /// </summary>
    public SurfaceRenderer() 
      :base()
    { }




    //public SurfaceRenderer(BitmapMap map, RenderStyle s)
    //  :base(map, s) { }






    public override void PrepareToRender()
    {
      base.PrepareToRender();

      //_outpen = new Pen((Color)_style["Color"], (int)_style["LineWidth"]);
      //_outFillBrush = new SolidBrush((Color)_style["FillColor"]);

    }






    public override DomainGeometryTypeEnum RendererGeometryDomain
    {
      get { return DomainGeometryTypeEnum.Surface; }
    }



    //todo:update...
    public override bool IsRenderable(GeometryBase geometry)
    {
      return true ;
    }




    /// <summary>
    /// main surface renderer entry function that causes input geometry
    /// transform and then renders each component of geometry to output
    /// </summary>
    /// <param name="geometry">input geometry</param>
    /// <param name="forwardTransform">transformation, normally from data {geometry} CS to map CS</param>
    /// <param name="reverseTransform">placeholder for reverse transform, not used here, null is fine</param>
    public override void RenderGeometry(GeometryBase geometry)
    {
      // do transform first and then parse geometry elements
      renderGeometry(_forwardTransform.Transform(geometry));
    }





    protected void renderGeometry(GeometryBase geom)
    {
      
      // ok try to find what is inside of geom
      if (geom is LineRing)
      {
        renderRing((LineRing)geom);
      }
      else if (geom is PathRing)
      {
        renderPathRing((PathRing)geom);
      }
      else if (geom is Polygon)
      {
        if (((Polygon)geom).Count == 1)
          renderGeometry(((Polygon)geom)[0]);
        else
          renderPolygon((Polygon)geom);
      }
      else if (geom is MultiArea)
        foreach (GeometryBase g in (MultiArea)geom)
          renderGeometry(g);

      else if (geom is GeometryCollection)
        foreach (GeometryBase g in (GeometryCollection)geom)
          renderGeometry(g);

    }





    // draw LineRing
    private void renderRing(LineRing geom)
    {

      //_PointIndex = 0;
      resetPointArray();

      //if something is ready to output...
      if (geometryToPoints(geom.coordList.getBuffer(), geom.coordList.position, geom.coordList.Count, geom.coordList.dimension))
      {

        //finalize point array
        trimPointArrayForSurface();

        // draw to output
        _outGraphic.FillPolygon(prepareBrush(), outPts);
        _outGraphic.DrawPolygon(preparePen(), outPts);

      }

    }






    // draws composite curve
    private void renderPathRing(Petra.Gis.Geometry.PathRing geom)
    {

      // if something is ready to output...
      if (preparePathRingPoints(geom))
      {

        trimPointArrayForSurface();

        _outGraphic.FillPolygon(prepareBrush(), outPts);
        _outGraphic.DrawPolygon(preparePen(), outPts);
      }

    }





    private bool prepareLineRing(LineRing g)
    {
      // reset point array ptr
      //_PointIndex = 0;
      resetPointArray();

      // if something is ready to output...
      // in this case dont draw directly
      // points might represent hole
      if (geometryToPoints(g.coordList.getBuffer(), g.coordList.position, g.coordList.Count, g.coordList.dimension))
      {
        trimPointArrayForSurface();

        return true;
      }

      // no output
      return false;
    }






    //private System.Drawing.Point[] preparePathRingPoints(PathRing geom)
    private bool preparePathRingPoints(PathRing geom)
    {

      bool hasOutp = false;

      // reset pointer
      //_PointIndex = 0;
      resetPointArray();

      foreach (GeometryBase g in geom)
      {
        if (g is LineString)
          hasOutp = geometryToPoints(((LineString)g).coordList.getBuffer(), ((LineString)g).coordList.position, ((LineString)g).coordList.Count, ((LineString)g).coordList.dimension);
        else if (g is Arc)
        {
          //stroke arc first
          LineString l = ArcMath.getStrokedArc((Arc)g);
          hasOutp = hasOutp | geometryToPoints(l.coordList.getBuffer(), l.coordList.position, l.coordList.Count, l.coordList.dimension);
        }

      }

      // if something is ready to output...
      if (hasOutp)
      {
        trimPointArrayForSurface();
        return true;
      }
      else
        return false;
    
    }





    private void renderPolygon(Polygon geom)
    {

      bool hasOutp = false;
      bool canDraw = false;
      GraphicsPath gp = new GraphicsPath();

      foreach (GeometryBase g in geom)
      {
        //_PointIndex = 0;
        if (g is LineRing)
          hasOutp = prepareLineRing((LineRing)g);
        else if (g is PathRing)
          hasOutp = preparePathRingPoints((PathRing)g);

        if (hasOutp)
        {
          gp.AddPolygon(outPts);
          canDraw = true;
        }
      }

      if (canDraw)
      {

        _outGraphic.FillPath(prepareBrush(), gp);
        _outGraphic.DrawPath(preparePen(), gp);
      }
    }






    private void drawMultiArea(MultiLine geom)
    {
      foreach (GeometryBase g in geom)
        renderGeometry(g);

    }




    //geometrycollection
    private void drawMultiArea(GeometryCollection geom)
    {
      foreach (GeometryBase g in geom)
        renderGeometry(g);
    }





    /// <summary>
    /// trims output array to size of prepared geometry
    /// </summary>
    protected void trimPointArrayForSurface()
    {

      // ok something is ready to output...
      // if less than 3 points must add some extra...
      // geometry was to small for this scale or close to edge,
      // output colapses to only dot or line
      // put 1 or 2 extra points by repeating start point...
      if (_PointIndex == 1)
      {
        addPointToArray(outPts[0].X + 1, outPts[0].Y);
        addPointToArray(outPts[0].X, outPts[0].Y);
      }
      else if (_PointIndex == 2)
        addPointToArray(outPts[0].X, outPts[0].Y);

      if (outPts.Length > _PointIndex)
        Array.Resize<System.Drawing.Point>(ref outPts, _PointIndex);

    }




    //protected void drawToOutput(GraphicsPath gp)
    //{ 
      
    //}


    protected Pen preparePen()
    {

      if (renderStyle.HasVisualAttributes)
      {
        Color c;
        int lW;

        if (renderStyle.StyleComponents["Color"] is SmartStyleComponent)
          c = (Color)((SmartStyleComponent)renderStyle["Color"]).GetComponentByValue(_dataVisualAttributes[((SmartStyleComponent)renderStyle["Color"]).SourceAttributeName]);
        else
          c = (Color)renderStyle.StyleComponents["Color"];

        if (renderStyle.StyleComponents["LineWidth"] is SmartStyleComponent)
          lW = (int)((SmartStyleComponent)renderStyle["Color"]).GetComponentByValue(_dataVisualAttributes[((SmartStyleComponent)renderStyle["Color"]).SourceAttributeName]);
        else
          lW = (int)renderStyle.StyleComponents["LineWidth"];

        return new Pen(c, lW);
      }
      else
      {
        if(_outpen == null)
          _outpen = new Pen((Color)renderStyle["Color"], (int)renderStyle["LineWidth"]);

        return _outpen;
      }

    }



    protected Brush prepareBrush()
    {
      //_outFillBrush = new SolidBrush((Color)_style["FillColor"]);
      if (renderStyle.HasVisualAttributes)
      {
        Color c;
        if (renderStyle["FillColor"] is SmartStyleComponent)
          c = (Color)((SmartStyleComponent)renderStyle["Color"]).GetComponentByValue(_dataVisualAttributes[((SmartStyleComponent)renderStyle["Color"]).SourceAttributeName]);
        else
          c = (Color)renderStyle["FillColor"];

        return new SolidBrush((Color)renderStyle["FillColor"]);
      }
      else
      {
        if (_outpen == null)
          _outFillBrush = new SolidBrush((Color)renderStyle["FillColor"]);

        return _outFillBrush;
      }
    }


  }
}
